/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦，本文采用木兰宽松许可证第2版]
 * 
 * https://zhiqim.org/project/zhiqim_kit/zhiqim_git.htm
 *
 * ZhiqimGit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.git.command;

import org.zhiqim.git.Git;
import org.zhiqim.git.GitCommand;
import org.zhiqim.git.GitConfig;
import org.zhiqim.git.GitWalker;
import org.zhiqim.git.objer.objectid.ObjectId;
import org.zhiqim.git.objer.objectid.RevCommit;
import org.zhiqim.git.refer.Ref;
import org.zhiqim.git.refer.RefUpdateResult;
import org.zhiqim.git.refer.RefUpdater;
import org.zhiqim.git.util.GitValidates;

import org.zhiqim.kernel.util.Asserts;

/**
 * 分枝创建命令 http://www.kernel.org/pub/software/scm/git/docs/git-branch.html
 * 
 * @version v1.0.0 @author zhichenggang 2017-3-12 新建与整理
 */
public class BranchCreateCommand extends GitCommand<Ref>
{
    /** git branch [--set-upstream | --track | --no-track] */
    public enum SetupUpstreamMode {TRACK, NOTRACK, SET_UPSTREAM;}

    private String name;
    private boolean force = false;
    private SetupUpstreamMode upstreamMode;
    private String startPoint = HEAD;
    private RevCommit startCommit;

    public BranchCreateCommand(Git git)
    {
        super(git);
    }

    public BranchCreateCommand setName(String name)
    {
        this.name = name;
        return this;
    }

    /******************************************************************************/
    // 设置参数
    /******************************************************************************/

    public BranchCreateCommand setForce(boolean force)
    {
        this.force = force;
        return this;
    }

    public BranchCreateCommand setStartPoint(String startPoint)
    {
        this.startPoint = startPoint;
        this.startCommit = null;
        return this;
    }

    public BranchCreateCommand setStartPoint(RevCommit startPoint)
    {
        this.startCommit = startPoint;
        this.startPoint = null;
        return this;
    }

    public BranchCreateCommand setUpstreamMode(SetupUpstreamMode mode)
    {
        this.upstreamMode = mode;
        return this;
    }

    /******************************************************************************/
    // 预处理&执行
    /******************************************************************************/

    protected void preprocess()
    {
        Asserts.assertNotEmptyBlank(name, "创建分枝时，分枝名称不能为空白");
        Asserts.asserts(GitValidates.isValidRefName(P_REFS_HEADS_ + name), "创建分枝时，分枝名称不合法");
    }

    protected Ref execute() throws Exception
    {
        GitWalker walker = new GitWalker(git);

        //检查是否已存在分枝
        Ref refToCheck = git.findRef(name);
        boolean exists = refToCheck != null && refToCheck.getName().startsWith(P_REFS_HEADS_);
        if (!force && exists)
            throw new Exception("分枝已存在" + name);

        //检查起始点
        ObjectId startAt = getStartPointObjectId();
        String startPointFullName = null;
        if (startPoint != null)
        {
            Ref baseRef = git.findRef(startPoint);
            if (baseRef != null)
                startPointFullName = baseRef.getName();
        }

        //判断起始点，是一个提交，还是一个分枝，还是一个标签
        String refLogMessage;boolean isStartBranche = false;
        if (startPointFullName == null)
        {//起始点为提交
            String baseCommit;
            if (startCommit != null)
                baseCommit = startCommit.getShortMessage();
            else
            {
                RevCommit commit = git.resolve(getStartPointOrHead(), RevCommit.class);
                baseCommit = commit.getShortMessage();
            }
            
            if (exists)
                refLogMessage = "分枝：重新定位起始点到该提交[" + baseCommit + "]";
            else
                refLogMessage = "分枝: 以该提交[" + baseCommit + "]作为起始点创建新分枝";
        }
        else if (startPointFullName.startsWith(P_REFS_HEADS_) || startPointFullName.startsWith(P_REFS_REMOTES_))
        {//起始点为分枝
            isStartBranche = true;
            if (exists)
                refLogMessage = "分枝： 重新定位起始点到该分枝[" + startPointFullName + "]";
            else
                refLogMessage = "分枝： 以该分枝[" + startPointFullName + "]作为起始点创建新分枝";
        }
        else
        {//起始点为标签
            startAt = walker.peel(walker.parse(startAt));
            if (exists)
                refLogMessage = "分枝： 重新定位起始点到该标签[" + startPointFullName + "]";
            else
                refLogMessage = "分枝： 以该标签[" + startPointFullName + "]作为起始点创建新分枝";
        }

        //创建分枝到仓库
        RefUpdater updateRef = git.newUpdater(P_REFS_HEADS_ + name);
        updateRef.setNewObjectId(startAt);
        updateRef.setRefLogMessage(refLogMessage, false);
        RefUpdateResult updateResult;
        if (exists && force)
            updateResult = updateRef.forceUpdate();
        else
            updateResult = updateRef.update();

        if (!isSuccess(updateResult, exists))
            throw new Exception("创建分枝异常结果：" + updateResult.name());

        Ref result = git.findRef(name);
        if (result == null)
            throw new Exception("创建分枝未知异常");

        if (!isStartBranche)
        {//不是从其他分枝作为起始点创建的分枝
            return result;
        }

        //从其他分枝为起始点创建分枝时，要求检查是否需要更新配置文件
        boolean doConfigure;
        if (upstreamMode == SetupUpstreamMode.SET_UPSTREAM || upstreamMode == SetupUpstreamMode.TRACK)
        {//明确需要配置
            doConfigure = true;
        }
        else if (upstreamMode == SetupUpstreamMode.NOTRACK)
        {//明确不需要配置
            doConfigure = false;
        }
        else
        {//未指定情况下检查默认配置
            String autosetupflag = git.getConfig().getString(_SECTION_BRANCH_, _KEY_AUTO_SETUP_MERGE_);
            if ("false".equals(autosetupflag)){
                doConfigure = false;
            }else if ("always".equals(autosetupflag)){
                doConfigure = true;
            }else{//未作缺省配置时检查是否起始点是远程分枝，远程分枝要求配置
                doConfigure = startPointFullName.startsWith(P_REFS_REMOTES_);
            }
        }

        if (doConfigure)
        {//需要更新配置文件的情况，设置分枝两个参数remote和marge
            GitConfig config = git.getConfig();
            config.setString(_SECTION_BRANCH_, name, _KEY_REMOTE_, ".");
            config.setString(_SECTION_BRANCH_, name, _KEY_MERGE_, startPointFullName);
            config.save();
        }
        
        return result;
    }
    
    /******************************************************************************/
    // 私有方法
    /******************************************************************************/

    private ObjectId getStartPointObjectId() throws Exception
    {
        if (startCommit != null)
            return startCommit;

        String startPointOrHead = getStartPointOrHead();
        ObjectId result = git.resolveIdOrName(startPointOrHead);
        if (result == null)
            throw new Exception("引用无法加载：" + startPointOrHead);

        return result;
    }

    private String getStartPointOrHead()
    {
        return startPoint != null ? startPoint : HEAD;
    }
    
    private boolean isSuccess(RefUpdateResult updateResult, boolean exists)
    {
        switch (updateResult)
        {
        case NEW:
            return !exists;
        case NO_CHANGE:
        case FAST_FORWARD:
        case FORCED:
            return exists;
        default:
            return false;
        }
    }
}
